"""
Test app views.
"""
import os
import random
import datetime

from google.appengine.api.taskqueue import Task
from google.appengine.ext import ndb
from webapp2 import RequestHandler, cached_property
from webapp2_extras import jinja2
from dsbq.config import config
from dsbq.pipeline.bigquery import build_dataset_id_from_dataset
from dsbq.pipeline.upload import start_upload
from dsbq.pipeline.download import execute_bigquery_query
from dsbq.query import StatQuery
from dsbq.models import VALID_CONTEXT

from models import Review, RATINGS, CATEGORIES

class AbstractUserView(RequestHandler):
    """
    Parent class that handles template rendering.
    """
    @cached_property
    def jinja2(self):
        """ Returns a Jinja2 renderer cached in the app registry. """
        return jinja2.get_jinja2(app=self.app)

    def render_to_response(self, template, **context):
        """ Renders a template and writes the result to the response. """
        rv = self.jinja2.render_template(template, **context)
        self.response.write(rv)

class Index(AbstractUserView):
    """
    Index page
    """
    def get(self):
        self.render_to_response('index.html')

class Populate(AbstractUserView):
    """
    Creates some review entities.
    """
    NAMES = ['Al', 'Beth', 'Carl', 'Dora', 'Ed', 'Fran', 'Greg', 'Helen', 'Ivan', 'Jane']

    def get(self):
        self.render_to_response('populate.html', count=500)

    def post(self):
        count = int(self.request.POST.get('count', 500))
        entities = []
        for _ in range(0, count):
            kwargs = {
                'reviewer': random.choice(self.NAMES),
                'published': datetime.datetime.utcnow() - datetime.timedelta(days=random.randint(1, 50)),
                'rating': random.choice(RATINGS),
                'category': random.choice(CATEGORIES),
            }
            entity = Review(**kwargs)
            entities.append(entity)
            if len(entities) >= 100:
                ndb.put_multi(entities)
                entities = []
        if entities:
            ndb.put_multi(entities)
        self.response.write('<html><body><p>%s reviews created.</p><p>Next step: <a href="/backup">backup</a>.</p></body></html>' % count)

class Backup(AbstractUserView):
    """
    Backs up Review entities to Cloud Storage.
    """
    def get(self):
        is_dev_appserver = 'development' in os.environ.get('SERVER_SOFTWARE', '').lower()
        ds_admin_link = 'https://appengine.google.com/datastore/admin?&app_id=%(appid)s' % {
            'appid': os.environ.get('APPLICATION_ID', 's~localhost').lower()
        }
        self.render_to_response('backup.html', ds_admin_link=ds_admin_link)

    def post(self):
        # start up the Datastore Admin back up tool
        url = "/_ah/datastore_admin/backup.create?name=%(appid)s&filesystem=gs&gs_bucket_name=%(bucket)s/%(appid)s&queue=default&kind=%(kind)s" % {
            'appid': config.DATASET,
            'bucket': config.BUCKET_NAME,
            'kind': Review._get_kind(),
        }
        target = 'ah-builtin-python-bundle'
        task = Task(url=url, target=target, method='GET')
        task.add()
        self.response.write('<html><body><p>Backup started. Watch default queue for progress.</p><p>Next step: <a href="/upload">upload</a>.</p></body></html>')

class Upload(AbstractUserView):
    """
    Uploads backed up reviews to BigQuery.
    """
    def get(self):
        context = {
            'dataset_id': build_dataset_id_from_dataset(config.DATASET),
            'bigquery_console_link': 'https://bigquery.cloud.google.com/',
        }
        self.render_to_response('upload.html')

    def post(self):
        start_upload(Review._get_kind())
        self.response.write('<html><body><p>Upload started. Watch logs for progress.</p><p>Next step: <a href="/download">download</a>.</p></body></html>')

class Download(AbstractUserView):
    """
    Executes queries and downloads stats from BigQuery.
    """
    QUERY_1 = """
        SELECT
          COUNT(*) AS [STAT__total_reviews]
        FROM
          [%s.Review]
    """
    QUERY_2 = """
        SELECT
          STRFTIME_UTC_USEC(UTC_USEC_TO_DAY(published), '%%Y-%%m-%%d') AS [DIMENSION__date],
          COUNT(*) AS [STAT__reviews_by_date],
          AVG(rating) AS [STAT__average_rating_by_date]
        FROM
          [%s.Review]
        GROUP BY
          [DIMENSION__date]
        ORDER BY
          [DIMENSION__date] DESC
    """
    QUERY_3 = """
        SELECT
          rating AS [DIMENSION__rating],
          category as [DIMENSION__category],
          COUNT(*) AS [STAT__reviews_by_rating_and_category]
        FROM
          [%s.Review]
        GROUP BY
          [DIMENSION__rating],
          [DIMENSION__category]
        ORDER BY
          [DIMENSION__rating]
    """
    def get(self):
        self.render_to_response('download.html')

    def post(self):
        context = self.request.POST.get('context')
        if not context:
            self.response.write('<html><body><h4>You must provide a context.</h4></body></html>')
            return
        if not VALID_CONTEXT.match(context):
            self.response.write('<html><body><h4>Context must match %s.</h4></body></html>' % VALID_CONTEXT)
            return
        dataset_id = build_dataset_id_from_dataset(config.DATASET)
        execute_bigquery_query(self.QUERY_1 % dataset_id, context)
        execute_bigquery_query(self.QUERY_2 % dataset_id, context)
        execute_bigquery_query(self.QUERY_3 % dataset_id, context)
        self.response.write('<html><body><p>Stats download started. Watch logs for progress.</p><p>Next step: <a href="/query">query</a>.</p></body></html>')

class Query(AbstractUserView):
    """
    Executes some datastore queries and shows the output.
    """
    def get(self):
        self.render_to_response('query.html')

    def post(self):
        context = self.request.POST.get('context')
        if not context:
            self.response.write('<html><body><h4>You must provide a context.</h4></body></html>')
            return
        if not VALID_CONTEXT.match(context):
            self.response.write('<html><body><h4>Context must match %s.</h4></body></html>' % VALID_CONTEXT)
            return

        # build up our queries (non-blocking)
        total_reviews = StatQuery(context, 'total_reviews')
        reviews_by_date = StatQuery(context, 'reviews_by_date')
        average_rating_by_date = StatQuery(context, 'average_rating_by_date')
        rating_counts_for_service = StatQuery(context, 'reviews_by_rating_and_category')
        rating_counts_for_service.add_dimension_filter('category', 'service')
        rating_counts_for_food = StatQuery(context, 'reviews_by_rating_and_category')
        rating_counts_for_food.add_dimension_filter('category', 'food')

        # execute the async queries in parallel
        StatQuery.run_all([total_reviews, reviews_by_date, average_rating_by_date,
                           rating_counts_for_service, rating_counts_for_food],
                          order=True)

        average_rating_by_date_map = {s.get_dimension_value('date'): float(s.value) for s in average_rating_by_date}

        context = {
            'total_reviews': list(total_reviews)[0], # exactly one result
            'reviews_by_date': reviews_by_date,
            'average_rating_by_date_map': average_rating_by_date_map,
            'rating_counts_for_service': rating_counts_for_service,
            'rating_counts_for_food': rating_counts_for_food
        }
        self.render_to_response('query-result.html', **context)
